import React from "react";
import PropTypes from "prop-types";
import TreeBase from "./TreeBase";
import {
    cloneDeep,
    findIndex,
    forEach, forIn,
    indexOf,
    isArray,
    isEmpty,
    isFunction,
    isObject, isUndefined,
    keys,
    map,
    omit
} from "lodash-es";
import styles from "./index.module.less";
import {Button, Input, Menu, Modal, Popconfirm, Popover, Tooltip, Tree} from "antd";
import {getRandomKey, isNotEmpty, recursiveFind, recursiveMap} from "jh-utils";
import {DeleteTwoTone, PlusCircleTwoTone, EditTwoTone} from "@ant-design/icons";
import {jhConfirm} from "../../utils/base";
import keyCodes from "../../keyCodes";
import {FieldCtrl} from "../Config";
import {ContextMenu, ContextMenuTrigger, hideMenu} from "react-contextmenu";
import InputComp from "../InputComp";

/**
 * 可编辑的树组件。
 * （1）数据源：state.list 为对象数组，以 props.compData 为初始值。其中对象数据结构如下：
 *      {
 *          key: '节点的唯一标识，一般取 isn',
 *          title: '节点名称，默认等于 name',
 *          name: '节点名称',
 *          code?: '节点的业务数据编码，一般等于 isn',
 *          isn:'节点唯一编码，用于接口保存',
 *          parentIsn:'父节点的 isn',
 *          CAN_ADD:'是否可以[添加子节点]',
 *          CAN_DEL:'是否可以[删除自己]'
 *      }
 * （2）操作：增删查改、移动、搜索。
 */
export default class EditableTree extends TreeBase {

    static propTypes = {
        ...TreeBase.propTypes,
        /**
         * 是否可以[添加根节点]
         */
        canAddRoot: PropTypes.bool,
        /**
         * 添加节点的回调。当添加一个节点时，回调此方法。
         * @type {function(node: object, moreData: {treeData: object[], parentNode})}
         * @return {{}|boolean} 可 return 一个新对象，作为新增节点的值。return false 可阻止本次更新。
         */
        onAddNode: PropTypes.func,
        afterAddNode: PropTypes.func,
        /**
         * 保存节点的回调。当节点名称被编辑保存之后，回调此方法。
         * @type {function(node: object, moreData: {treeData: object[]})}
         */
        onSaveNode: PropTypes.func,
        afterSaveNode: PropTypes.func,
        /**
         * 删除节点的回调。当节点被删除之后，回调此方法。
         * @type {function(node: object, moreData: {treeData: object[]})}
         */
        onDeleteNode: PropTypes.func,
        afterDeleteNode: PropTypes.func,
        /**
         * 点击右键菜单的“根节点”后的回调。
         */
        onEditRootNode: PropTypes.func,
        /**
         * 点击编辑，返回当前"根节点"树的回调
         */
        onEditNode: PropTypes.func,
    };

    static defaultProps = {
        ...TreeBase.defaultProps,
        border: true
    };

    /**
     * 删除的记录
     * @type {*[]}
     */
    deletedIds = [];

    constructor(props) {
        super(props);
    }

    initState(props) {
        return {
            ...super.initState(props),
            editingKey: '', // 正在编辑的节点 key
        }
    }

    componentDidMount() {
        super.componentDidMount();

        const {fieldName, appins, customprops} = this.props;
        if (appins && fieldName) {
            // 此组件是作为应用的字段使用，则将组件的实例存到 appins.fieldInsObj 中，用于按需调用
            isFunction(appins.setFieldInsByName) && appins.setFieldInsByName(fieldName, this, customprops);
        }
    }

    getNewTreeNode() {
        let name = 'node name';
        let key = getRandomKey();
        return {
            name,
            title: name,
            key,
            isn: key,
            parentIsn: '',
            code: key,
            CHANGED: true
        }
    }

    /**
     *
     * @param onlyChange 只取有变化的值。默认 true
     * @return {[]}
     */
    // getAjaxData(onlyChange = true) {
    //
    //     let reData = [];
    //
    //     if (onlyChange) {// 只取有变化的值
    //         reData = cloneDeep(this.state.list);
    //
    //         // forEach(reData, (dataItem) => {
    //         //
    //         //     if (dataItem.CHANGED) {
    //         //         reData.push(omit(dataItem, ['CHANGED']));
    //         //     }
    //         //
    //         // });
    //
    //     } else { // 取当前列表的所有值
    //         reData = recursiveMap(this.state.list, (dataItem) => (omit(dataItem, ['CHANGED'])));
    //     }
    //
    //     return reData;
    // }

    /**
     * 获取删除了的记录
     * @param fullRecord boolean 是否需要返回完整的记录。默认 false，只返回删除了的 id 数组
     * @return {[]}
     */
    getDeleted(fullRecord = false) {
        let reData = [];

        recursiveFind(this.state.originList, (dataItem) => {
            if (dataItem && -1 !== indexOf(this.deletedIds, dataItem.id)) {

                if (fullRecord) {
                    reData.push(dataItem);
                } else {
                    reData.push(dataItem.id);
                }
            }
        });

        return reData;
    }

    /**
     * 添加删除的记录 id
     * @param ids ｛[number]｝ id 数组
     */
    addDeletedIds(ids) {
        if (!isEmpty(ids)) {
            this.deletedIds = this.deletedIds.concat(ids);
        }
    }

    resetData(treeData, afterCb) {
        this.deletedIds = [];
        super.resetData(treeData, afterCb);
    }

    /**
     * 校验每行的必填字段。
     * （在 afc 的 getAjaxData4Save 方法调用）
     * @param onlyChange {boolean} 此字段无用，只是为了与 DetailTable.verifyRequired 方法的调用方式统一。
     * @param moreProps
     * @param moreProps.local {string} 当前的语言
     * @param moreProps.data {[]} 如果传入这个参数，则以这个参数的值作为校验的数据源。否则默认取当前表格的数据进行校验
     * @return {{data: [], errors: []}}
     */
    verifyRequired(onlyChange, {local, data}) {
        let isByData = !isUndefined(data);
        const dataList = isByData ? data : this.getData();
        const errors = [];
        const requiredFields = this.requiredFields;

        if (!isEmpty(requiredFields)) {
            let localKey = 'en-US' !== local ? 'cn' : 'en';

            recursiveMap(dataList, (node, ind, parentNode) => {
                node.REMARK_KEY = `${!isEmpty(parentNode) ? parentNode.name + '->' : ''}${node.name}`;
                let errFields = []; // 有错误的字段的文本描述

                forEach(requiredFields, (fObj, fieldName) => {

                    if (!isNotEmpty(node[fieldName])) {
                        errFields.push(fObj[localKey] || fieldName);
                    }
                });

                if (!isEmpty(errFields)) {
                    // 't.verify.empty': '树节点【{node}】:【{fields}】不能为空',
                    errors.push(this.myFormatMessage('t.verify.empty', {
                        node: node.REMARK_KEY,
                        fields: errFields.join('、')
                    }));
                }

                return node;
            });
        }
        return {data: dataList, errors};
    }

    /**
     * 在 pKey 下添加子节点
     * @param pKey
     */
    hdlAddChildNode(pKey) {
        // const newList = this.state.list;
        const newExpandKeys = [...this.state.expandedKeys];
        let newEditingKey = '';
        let tmpObj = this.getNewTreeNode();
        let parentNode = null;
        let canAdd = true;

        // const newList = cloneDeep(this.state.list);
        recursiveFind(this.state.list, (item, ind, pItem) => {

            if (item.key === pKey) {
                tmpObj.parentIsn = item.key;

                let newObj = this.runPropsFunc('onAddNode')(tmpObj, {treeData: this.state.list, parentNode: item});

                if (false === newObj) {
                    canAdd = false;
                }

                if (isObject(newObj)) {
                    tmpObj = newObj;
                }

                if (canAdd) {
                    if (!isArray(item.children)) {
                        item.children = [];
                    }
                    // tmpObj.order = item.children.length + 1;
                    tmpObj.order = this.setOrder(item.children) + 1;
                    tmpObj.showOrder = tmpObj.order;
                    item.children.push(tmpObj);
                    newExpandKeys.push(item.key);
                    newEditingKey = tmpObj.key;
                    parentNode = item;
                }
                return true;
            }

            return false;
        });

        if (!canAdd) return false;

        setTimeout(() => {
            // console.log('list', this.state.list);

            this.setState({
                expandedKeys: newExpandKeys, autoExpandParent: true, editingKey: newEditingKey
            }, () => {
                this.runPropsFunc('afterAddNode')(tmpObj, {treeData: this.state.list, parentNode})
                // if (isFunction(this.props.afterAddNode)) {
                //     this.props.afterAddNode(tmpObj, {treeData: this.state.list, parentNode});
                // }
            });
        }, 0);
    }

    /**
     * 添加与 key 同级的节点
     */
    hdlAddNode(key) {
        let tmpObj = this.getNewTreeNode();
        let parentNode = null;

        recursiveFind(this.state.list, (item, ind, pItem) => {

            if (item.key === key) {
                parentNode = pItem;
                tmpObj.parentIsn = item.parentIsn;
                return true;
            }

            return false;
        });

        // 如果不允许添加根节点，且当前无选中节点或选中根节点，则不允许添加节点
        if (false === this.getPropsByName('canAddRoot') && isEmpty(parentNode)) {
            return false;
        }

        let newObj = this.runPropsFunc('onAddNode')(tmpObj, {treeData: this.state.list, parentNode});
        if (false === newObj) return false;

        if (isObject(newObj)) {
            tmpObj = newObj;
        }
        let editingKey = tmpObj.key;

        if (parentNode) {

            if (!isArray(parentNode.children)) {
                parentNode.children = [];
            }
            // tmpObj.order = parentNode.children.length + 1;
            tmpObj.order = this.setOrder(parentNode.children) + 1;
            tmpObj.showOrder = tmpObj.order;
            parentNode.children.push(tmpObj);

        } else {
            // 当前无选中节点，则直接在 state.list 添加节点
            if (!isArray(this.state.list)) {
                this.state.list = [];
            }
            // tmpObj.order = this.state.list.length + 1;
            tmpObj.order = this.setOrder(this.state.list) + 1;
            tmpObj.showOrder = tmpObj.order;
            this.state.list.push(tmpObj);
        }

        setTimeout(() => {

            this.setState({
                editingKey
            }, () => {
                this.runPropsFunc('afterAddNode')(tmpObj, {treeData: this.state.list, parentNode});
                // if (isFunction(this.props.afterAddNode)) {
                //     this.props.afterAddNode(tmpObj, {treeData: this.state.list, parentNode});
                // }
            });
        }, 0);
    }

    /**
     * 设置节点order值
     * @param list
     */
    setOrder(list) {
        let newOrder = 0;
        if (!isEmpty(list)) {
            let preNode = list[list.length - 1];
            if (!isEmpty(preNode) && preNode.order) {
                newOrder = Number(preNode.order);
                return newOrder;
            }
            return list.length;
        }
        return newOrder;
    }

    hdlEditNode(key) {
        this.setState({editingKey: key});

        //点击节点，回调给外部使用
        if (this.props.onEditNode)
            this.props.onEditNode(key);
    }

    hdlDeleteNode(key, pNode) {
        if (key) {
            let delNode = null;

            const newList = cloneDeep(this.state.list);
            let newPItem = pNode ? recursiveFind(newList, (nItem) => (nItem && nItem.key === pNode.key)) : null;

            recursiveMap(this.state.list, (item, ind, pItem) => {

                if (item.key === key) {

                    if (isEmpty(newPItem) && pItem) {
                        newPItem = recursiveFind(newList, (nItem) => (nItem && nItem.key === pItem.key));
                    }

                    if (newPItem && isArray(newPItem.children)) {
                        newPItem.children.splice(ind, 1);
                    } else {
                        // 没有 pItem，说明是第一级树节点，直接从 newList 删除
                        newList.splice(ind, 1);
                    }
                    delNode = item;
                }

                return item;
            });
            let parentNode = newPItem;

            if (false === this.runPropsFunc('onDeleteNode')(delNode, {treeData: this.state.list, parentNode}))
                return false;

            // 记录“删除的节点及其所有子节点中，有 id 的节点”的 id 值
            if (delNode && delNode.id > 0) {
                this.deletedIds.push(delNode.id);

                if (!isEmpty(delNode.children)) {
                    recursiveMap(delNode.children, (chNode) => {
                        if (chNode && chNode.id > 0) {
                            this.deletedIds.push(chNode.id);
                        }
                        return chNode;
                    })
                }
            }

            setTimeout(() => {
                // 从 已选中/已展开 的数组中移除此节点
                const newState = {list: newList};
                const delIndInSelected = indexOf(this.state.selectedKeys, key);
                const delIndInExpanded = indexOf(this.state.expandedKeys, key);

                if (delIndInSelected > -1) {
                    const newKeys = [...this.state.selectedKeys];
                    newKeys.splice(delIndInSelected, 1);
                    newState.selectedKeys = newKeys;
                }
                if (delIndInExpanded > -1) {
                    const newKeys = [...this.state.expandedKeys];
                    newKeys.splice(delIndInExpanded, 1);
                    newState.expandedKeys = newKeys;
                }

                this.setState(newState, () => {
                    this.runPropsFunc('afterDeleteNode')(delNode, {treeData: this.state.list, parentNode});
                    // if (isFunction(this.props.afterDeleteNode)) {
                    //     this.props.afterDeleteNode(delNode, {treeData: this.state.list, parentNode});
                    // }
                });
            }, 0);
        }
    }

    hdlSaveInput(node, val, parentNode) {

        if (this.hasPropFunc('onSaveNode')) {
            let newNode = this.runPropsFunc('onSaveNode')(node, {treeData: this.state.list, parentNode});

            if (false === newNode) {
                this.setState({editingKey: ''});
                return false;
            }

            forIn(newNode, (v, k) => {
                node[k] = v;
            });
        } else {
            node.name = val;
        }

        this.setState({editingKey: ''}, () => {
            this.runPropsFunc('afterSaveNode')(node, {treeData: this.state.list, parentNode});
        });
    }

    renderTree(treeProps, context) {
        const {editingKey, selectedKeys} = this.state;
        const {treeData, fieldCtrl, disabled, canAddRoot, doubleClick} = treeProps;
        let selectedKey = selectedKeys ? selectedKeys[0] || '' : '';
        let selectedNode = null;
        let nodeIs8 = this.formatMsgByCn('节点');
        let addChildTxt = `${this.formatMsgByCn('添加')}${this.formatMsgByCn('子节点')}`;
        let addNodeTxt = `${this.formatMsgByCn('添加同级节点')}`;
        let delNodeTxt = `${this.formatMsgByCn('删除')}${nodeIs8}`;
        let editNodeTxt = `${this.formatMsgByCn('重命名')}${nodeIs8}`;
        let rootNodeTxt = `${this.formatMsgByCn('编辑根节点')}`;

        const newTreeData = recursiveMap(treeData, (item, ind, pItem) => {
            if (selectedKey === item.key) {
                selectedNode = item;
            }

            const reObj = {
                ...item
            };

            if (fieldCtrl === FieldCtrl.READONLY) {
                reObj.title = reObj.name;
            } else {
                reObj.title = (
                    <div className={styles['outlineLine']}
                         onDoubleClick={(e) => {
                             if (false !== doubleClick) {
                                 this.hdlEditNode(item.key);
                             }
                         }}>
                        {
                            (editingKey === item.key && false !== doubleClick) ? (
                                <InputComp className={styles.nodeInput}
                                           autoFocus={true}
                                           size={"small"}
                                           defaultValue={item.name}
                                           onKeyDown={(e) => {
                                               e.stopPropagation();
                                               let keyCode = e.keyCode || e.charCode;
                                               if (keyCodes.esc === keyCode) {
                                                   this.setState({editingKey: ''});
                                               }
                                           }}
                                           onPressEnter={(e) => {
                                               this.hdlSaveInput(item, e.target.value, pItem);
                                           }}
                                           onBlur={(e) => {
                                               this.hdlSaveInput(item, e.target.value, pItem);
                                           }}/>
                            ) : (
                                <>
                                    {item.name}
                                    <div className={styles['outlineButton']}>
                                        {
                                            false !== item.CAN_ADD && (
                                                <Button
                                                    title={addChildTxt}
                                                    type="link"
                                                    size="small"
                                                    onClick={e => {
                                                        // this.clickingAct = true;
                                                        this.hdlAddChildNode(item.key);
                                                    }}
                                                    icon={<PlusCircleTwoTone/>}
                                                >
                                                </Button>
                                            )
                                        }
                                        {
                                            true === item.CAN_EDIT && (
                                                <Button
                                                    title={this.formatMsgByCn('编辑')}
                                                    type="link"
                                                    size="small"
                                                    onClick={e => {
                                                        // this.clickingAct = true;
                                                        this.hdlEditNode(item.key);
                                                    }}
                                                    icon={<EditTwoTone/>}
                                                >
                                                </Button>
                                            )
                                        }
                                        {
                                            false !== item.CAN_DEL && (
                                                <Button
                                                    title={delNodeTxt}
                                                    type="link"
                                                    size="small"
                                                    onClick={(e) => {
                                                        this.clickingAct = true;
                                                        if (!isEmpty(item.children) || item.DEL_PROMPT === true) {
                                                            e.persist();
                                                            jhConfirm({
                                                                title: this.myFormatMessage('crud.tip.delete'),
                                                                onOk: () => {
                                                                    this.hdlDeleteNode(item.key, pItem)
                                                                }
                                                            })
                                                        } else {
                                                            this.hdlDeleteNode(item.key, pItem)
                                                        }
                                                    }}
                                                    icon={<DeleteTwoTone twoToneColor={"red"}/>}
                                                >
                                                </Button>
                                            )
                                        }

                                    </div>
                                </>
                            )
                        }
                    </div>
                );
            }
            ;
            //添加气泡卡片
            if (reObj.isPopover) {
                reObj.title = (
                    <Popover {...reObj.popProps}>
                        {reObj.title}
                    </Popover>
                )
            }

            return reObj;
        });
        const noSelected = isEmpty(selectedNode);
        // 如果当前组件只读、或者（不允许添加根节点、且（无选中节点或选中根节点）），则禁止“添加同级节点”
        const disabledAddBroNode = disabled || (
            false === canAddRoot && (noSelected || isEmpty(selectedNode.parentIsn))
        );

        const extraMenu = (<Menu style={{minWidth: 150}} selectable={false} onClick={() => {
            hideMenu();
        }}>
            {
                false !== canAddRoot && (
                    <Menu.Item onClick={(e) => { // 编辑根节点
                        this.setState({
                            selectedKeys: []
                        }, () => {
                            this.runPropsFunc('onEditRootNode')({treeData: this.state.list});
                        });
                    }}>{rootNodeTxt}</Menu.Item>
                )
            }
            {
                false !== canAddRoot && (
                    <Menu.Divider/>
                )
            }
            <Menu.Item disabled={disabledAddBroNode} onClick={(e) => { // 添加同级节点
                this.hdlAddNode(selectedKey)
            }}>{addNodeTxt}</Menu.Item>
            <Menu.Item disabled={disabled || noSelected || (selectedNode && false === selectedNode.CAN_ADD)}
                       onClick={(e) => { // 添加子节点
                           this.hdlAddChildNode(selectedKey)
                       }}>{addChildTxt}</Menu.Item>
            <Menu.Item disabled={disabled || noSelected} onClick={(e) => { // 编辑节点
                this.hdlEditNode(selectedKey);
            }}>{editNodeTxt}</Menu.Item>
            <Menu.Item disabled={disabled || noSelected || (selectedNode && false === selectedNode.CAN_DEL)}
                       onClick={(e) => { // 删除
                           this.hdlDeleteNode(selectedKey)
                       }}>{delNodeTxt}</Menu.Item>
        </Menu>);

        return (<>
                <ContextMenuTrigger
                    id={this.contextMenuId}> {/* NOTICE: id must be unique for EVERY instance */}
                    <Tree
                        {...treeProps}
                        treeData={newTreeData}
                        height={'100%'}
                    />
                </ContextMenuTrigger>

                <ContextMenu id={this.contextMenuId}>
                    {extraMenu}
                </ContextMenu>
            </>
        );
    }
}